﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MySql.Data.MySqlClient;
using MySql.Data;
using MySql.Web;

namespace Artemis
{

    public partial class navigation : Form
    {
        private Int32 vertex_num;
        private Edge[] head;
        public void rebuild(Int32 vertex_num)
        {
            this.vertex_num = vertex_num;
            this.head = new Edge[vertex_num];
        }
        public void addEdge(Int32 from, Int32 to, Distance distance)
        {
            var edge = new Edge(from, to, distance);
            edge.next = head[from];
            head[from] = edge;
        }
        public Edges edges(Int32 vertex)
        {
            return new Edges(head[vertex]);
        }
        public Tuple<Distance[], Int32[]> spfa(Int32 source)
        {
            var queue = new Queue<Int32>();
            var distances = new Distance[vertex_num];
            var precursor = new Int32[vertex_num];
            var in_queue = new Boolean[vertex_num];
            for (var i = 0; i < vertex_num; ++i)
            {
                distances[i] = Distance.MAX_VALUE;
            }
            distances[source] = default(Distance);
            precursor[source] = -1;
            queue.Enqueue(source);
            in_queue[source] = true;
            for (; queue.Count > 0; )
            {
                var from = queue.Dequeue();
                in_queue[from] = false;
                foreach (var edge in edges(from))
                {
                    Distance sum = distances[edge.from] + edge.distance;
                    if (sum < distances[edge.to])
                    {
                        distances[edge.to] = sum;
                        precursor[edge.to] = edge.from;
                        if (!in_queue[edge.to])
                        {
                            queue.Enqueue(edge.to);
                            in_queue[edge.to] = true;
                        }
                    }
                }
            }
            return new Tuple<Distance[], Int32[]>(distances, precursor);
        }

        public string connectionstring = null;
        public string oriID = null;
        public string destID = null;
        public bool oriconfirmed = false;
        public navigation(string Connectionstring)
        {
            InitializeComponent();
            connectionstring = Connectionstring;
            rebuild(0);
        }
        public navigation(string Connectionstring,Int32 vertex_num)
        {
            rebuild(vertex_num);
            connectionstring = Connectionstring;
            InitializeComponent();
        }

        private void oriconfirm_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void oriconf_Click(object sender, EventArgs e)
        {
            try
            {
                string originsys = null;
                originsys = origin.Text;
                DataSet originsystem = new DataSet();
                string sql = "select solarSystemID from mapsolarsystems where solarSystemName = @originsys";
                MySqlCommand sysIDquery = new MySqlCommand(sql);
                sysIDquery.Parameters.Add(new MySqlParameter("@originsys", originsys));
                originsystem = EveCentral.getdata(sysIDquery, "sysID", connectionstring);
                if (originsystem != null && originsystem.Tables[0].Rows.Count != 0)
                {
                    MessageBox.Show("输入出发星系为"+originsys+"\n已确认");
                    oriID = originsystem.Tables["sysID"].Rows[0]["sysID"].ToString();
                    oriconfirmed = true;
                }
                else
                {
                    MessageBox.Show("输入的不是有效星系名称");
                }
                if (oriconfirmed == true)
                {
                    destconf.Enabled = true;
                }
                if (oriconfirmed == false)
                {
                    destconf.Enabled = false;
                }


            }
            catch (Exception)
            {
                MessageBox.Show("Fatal Error");
                Close();
            }
        }

        private void destconf_Click(object sender, EventArgs e)
        {
            try
            {
                string destsys = null;
                destsys = dest.Text;
                DataSet destsystem = new DataSet();
                string sql = "select solarSystemID from invsys where syName = @destsys";
                MySqlCommand sysIDquery = new MySqlCommand(sql);
                sysIDquery.Parameters.Add(new MySqlParameter("@destsys", destsys));
                destsystem = EveCentral.getdata(sysIDquery, "sysID", connectionstring);
                if (destsystem != null && destsystem.Tables[0].Rows.Count != 0)
                {
                    MessageBox.Show("输入目标星系为" + destsys + "\n已确认");
                    destID = destsystem.Tables["sysID"].Rows[0]["sysID"].ToString();
                }
                else
                {
                    MessageBox.Show("输入的不是有效星系名称");
                }
                MySqlConnection connection = new MySqlConnection("Server=bromine0x23.chinacloudapp.cn;User=user;Charset=utf8");
                connection.Open();

                MySqlCommand command = new MySqlCommand();
                MySqlDataReader reader;
                command.Connection = connection;

                command.CommandText = "SELECT MIN(`stargateID`),MAX(`stargateID`)FROM`EVE`.`stargates`";
                Int32 minStargateID = 0;
                Int32 maxStargateID = 0;
                for (reader = command.ExecuteReader(); reader.Read(); )
                {
                    minStargateID = reader.GetInt32(0);
                    maxStargateID = reader.GetInt32(1);
                }
                reader.Close();

                var graph = new navigation(connectionstring,maxStargateID - minStargateID + 1);

                command.CommandText =
    "SELECT" +
        "`fromStargates`.`stargateID`AS`fromStargateID`,`toStargates`.`stargateID`AS`toStargateID`," +
        "SQRT(" +
            "(`fromStargates`.`x`-`toStargates`.`x`) * (`fromStargates`.`x`-`toStargates`.`x`) + " +
            "(`fromStargates`.`y`-`toStargates`.`y`) * (`fromStargates`.`y`-`toStargates`.`y`) + " +
            "(`fromStargates`.`z`-`toStargates`.`z`) * (`fromStargates`.`z`-`toStargates`.`z`) " +
        ") / 149597870700 AS`distance`" +
    "FROM`EVE`.`stargates`AS`fromStargates`" +
        "JOIN`EVE`.`stargates`AS`toStargates`" +
            "ON`fromStargates`.`solarSystemID`=`toStargates`.`solarSystemID`" +
            "AND`fromStargates`.`stargateID`<>`toStargates`.`stargateID`";
                for (reader = command.ExecuteReader(); reader.Read(); )
                {
                    graph.addEdge(reader.GetInt32(0) - minStargateID, reader.GetInt32(1) - minStargateID, new Distance(0, reader.GetDouble(2)));
                }
                reader.Close();

                command.CommandText =
    "SELECT " +
        "`fromStargateID`,`toStargateID`" +
    "FROM" +
        "`EVE`.`stargateJumps`;";
                for (reader = command.ExecuteReader(); reader.Read(); )
                {
                    graph.addEdge(reader.GetInt32(0) - minStargateID, reader.GetInt32(1) - minStargateID, new Distance(1, 0));
                }
                reader.Close();

                command.CommandText =
    "SELECT`stargateID`" +
    "FROM`EVE`.`stargates`" +
    "WHERE`solarSystemID` = @sysID";
                command.Parameters.AddWithValue("@sysID",oriID);
                var startStargateIDs = new List<Int32>();
                for (reader = command.ExecuteReader(); reader.Read(); )
                {
                    startStargateIDs.Add(reader.GetInt32(0) - minStargateID);
                }
                reader.Close();

                command.Parameters.Clear();
                command.Parameters.AddWithValue("@sysID", destID);
                var endStargateIDs = new List<Int32>();
                for (reader = command.ExecuteReader(); reader.Read(); )
                {
                    endStargateIDs.Add(reader.GetInt32(0) - minStargateID);
                }
                reader.Close();

                var minDistance = Distance.MAX_VALUE;
                Int32[] minPrecursor = null;
                Int32 destination = -1;

                foreach (var startStargateID in startStargateIDs)
                {
                    var result = graph.spfa(startStargateID);
                    var distance = result.Item1;
                    var precursor = result.Item2;
                    foreach (var endStargateID in endStargateIDs)
                    {
                        if (distance[endStargateID] < minDistance)
                        {
                            destination = endStargateID;
                            minDistance = distance[destination];
                            minPrecursor = precursor;
                        }
                    }
                }

                command.CommandText =
    "SELECT `solarSystemNameZH`" +
    "FROM`EVE`.`stargates`" +
        "JOIN`EVE`.`solarSystems`" +
            "ON`stargates`.`solarSystemID`=`solarSystems`.`solarSystemID`" +
    "WHERE`stargateID`=@stargateID";
                List<object> names = new List<object>();
                for (Int32 current = destination; current != -1; current = minPrecursor[current])
                {
                    command.Parameters.Clear();
                    command.Parameters.AddWithValue("stargateID", current + minStargateID);
                    String name = command.ExecuteScalar() as String;
                    names.Add(name);
                
                }
                richTextBox1.Text = null;
                for (int i = names.Count-1; i >= 0; i--)
                {
                    if (i != 0 && names[i].ToString() != names[i - 1].ToString())
                    {
                        richTextBox1.AppendText(names[i].ToString() + "\n");
                    }
                    if (i == 0)
                    {
                        richTextBox1.AppendText(names[i].ToString() + "\n");
                    }

                }

                    connection.Close();



            }
            catch (Exception)
            {
                MessageBox.Show("Fatal Error");
                Close();
            }

        }

        private void origin_TextChanged(object sender, EventArgs e)
        {
            destconf.Enabled = false;
        }
    }
    public struct Distance
    {
        public Int64 jump_num;
        public Double jump_dist;
        public static Distance MAX_VALUE = new Distance(Int32.MaxValue, Double.MaxValue);
        public Distance(Int64 jump_num, Double jump_dist)
        {
            this.jump_num = jump_num;
            this.jump_dist = jump_dist;
        }
        public static Distance operator +(Distance x, Distance y)
        {
            return new Distance(x.jump_num + y.jump_num, x.jump_dist + y.jump_dist);
        }
        public static Boolean operator <(Distance x, Distance y)
        {
            return (x.jump_num < y.jump_num) || (x.jump_num == y.jump_num && x.jump_dist < y.jump_dist);
        }
        public static Boolean operator >(Distance x, Distance y)
        {
            return (x.jump_num > y.jump_num) || (x.jump_num == y.jump_num && x.jump_dist > y.jump_dist);
        }
    }
    public class Edge
    {
        public Int32 from;
        public Int32 to;
        public Distance distance;
        public Edge next;
        public Edge()
        {
            this.from = -1;
            this.to = -1;
            this.distance = Distance.MAX_VALUE;
        }
        public Edge(Int32 from, Int32 to)
        {
            this.from = from;
            this.to = to;
            this.distance = Distance.MAX_VALUE;
        }
        public Edge(Int32 from, Int32 to, Distance distance)
        {
            this.from = from;
            this.to = to;
            this.distance = distance;
        }
    }
    public class EdgeEnumerator : IEnumerator<Edge>
    {
        private Edge head = new Edge();
        private Edge current_edge;
        public EdgeEnumerator(Edge edge)
        {
            head.next = edge;
            Reset();
        }
        public void Dispose() { }
        object IEnumerator.Current
        {
            get
            {
                return current_edge;
            }
        }
        public Edge Current
        {
            get
            {
                return current_edge;
            }
        }
        public bool MoveNext()
        {
            if (current_edge.next != null)
            {
                current_edge = current_edge.next;
                return true;
            }
            else
            {
                return false;
            }
        }
        public void Reset()
        {
            current_edge = head;
        }
    }
    public class Edges : IEnumerable<Edge>
    {
        private Edge edge;
        public Edges(Edge edge)
        {
            this.edge = edge;
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator() as IEnumerator;
        }
        IEnumerator<Edge> IEnumerable<Edge>.GetEnumerator()
        {
            return GetEnumerator() as IEnumerator<Edge>;
        }
        public EdgeEnumerator GetEnumerator()
        {
            return new EdgeEnumerator(edge);
        }
    }



}
